package cn.funeralobjects.dictms.service.impl;

import cn.funeralobjects.common.jpa.service.*;
import cn.funeralobjects.common.repository.*;
import cn.funeralobjects.common.service.annotataion.CommonService;
import cn.funeralobjects.common.service.exception.DataConflictException;
import cn.funeralobjects.common.service.validate.DataCheck;
import cn.funeralobjects.dictms.BasicInfo;
import cn.funeralobjects.dictms.entity.EDict;
import cn.funeralobjects.dictms.entity.EDictValue;
import cn.funeralobjects.dictms.model.DictValue;
import cn.funeralobjects.dictms.repository.EDictValueRepository;
import cn.funeralobjects.dictms.service.DictService;
import cn.funeralobjects.dictms.service.DictValueService;
import cn.funeralobjects.util.Assert;
import cn.funeralobjects.util.NumberUtils;
import cn.funeralobjects.util.annotation.ArgHasLength;
import cn.funeralobjects.util.annotation.ArgNonNull;
import cn.funeralobjects.util.annotation.ArgNotZero;
import cn.funeralobjects.util.annotation.AssertArg;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.repository.CrudRepository;

import javax.annotation.Resource;
import java.util.List;
import java.util.Optional;

/**
 * @author FuneralObjects
 * Create date: 2020/6/3 12:45 AM
 */
@CommonService
public class DictValueServiceImpl implements DictValueService,
        JpaModifyService<DictValue, EDictValue, Integer>,
        JpaEntityService<EDictValue, Integer>,
        JpaCodeService<EDictValue, Integer>,
        JpaEnableService<EDictValue, Integer>,
        JpaDelSaltService<EDictValue, Integer>,
        JpaCommitService<Integer> {

    @Resource
    private EDictValueRepository eDictValueRepository;

    @Resource
    private DictService dictService;

    @AssertArg
    @Override
    public void checkConflict(@AssertArg DictValue dictValue) throws DataConflictException {
        DataCheck.conflictData(this.existsByValueInDict(dictValue.getValue(), dictValue.getDictId()), () -> createDataCode("value", dictValue.getValue()));
    }

    @Override
    public EDictValue toEntity(DictValue dictValue) {
        if (dictValue == null) {
            return null;
        }
        EDict dict = new EDict();
        dict.setId(dictValue.getDictId());
        EDictValue value = new EDictValue();
        value.setValue(dictValue.getValue())
                .setCode(dictValue.getCode())
                .setRemark(dictValue.getRemark())
                .setDict(dict);
        return value;
    }

    @Override
    public CodeRepository<EDictValue, Integer> codeRepository() {
        return eDictValueRepository;
    }

    @Override
    public CommitRepository<Integer> commitRepository() {
        return eDictValueRepository;
    }

    @Override
    public DelSaltRepository<EDictValue, Integer> delSaltRepository() {
        return eDictValueRepository;
    }

    @Override
    public EnableRepository<EDictValue, Integer> enableRepository() {
        return eDictValueRepository;
    }

    @Override
    public EntityRepository<EDictValue, Integer> entityRepository() {
        return eDictValueRepository;
    }

    @Override
    public CrudRepository<EDictValue, Integer> modifyRepository() {
        return eDictValueRepository;
    }

    @AssertArg
    @Override
    public void checkConflict(@AssertArg DictValue dictValue, @ArgNotZero Integer withoutId) throws DataConflictException {
        DataCheck.conflictData(this.existsByValueInDict(dictValue.getValue(), dictValue.getDictId(), withoutId), () -> createDataCode("value", dictValue.getValue()));
    }

    @Override
    public void copyToEntity(DictValue sourceModify, EDictValue targetEntity) {
        targetEntity.setRemark(sourceModify.getRemark())
                .setCode(sourceModify.getCode())
                .setValue(sourceModify.getValue());
    }

    @Override
    public boolean isValid(Integer id) {
        return NumberUtils.notZero(id);
    }


    @Override
    public void validateModify(DictValue dictValue) {
        Assert.argAssert(dictValue, "dictValue");
        dictService.mustExistsById(dictValue.getDictId());
    }

    @Override
    public String getName() {
        return ENTITY_NAME;
    }

    @Override
    public String getModuleName() {
        return BasicInfo.MODULE_NAME;
    }

    @AssertArg
    @Override
    public boolean existsByValueInDict(@ArgHasLength String value, @ArgNotZero Integer dictId) {
        return eDictValueRepository.existsAllByDict_IdAndValueAndDelSalt(dictId, value, getExistsDelSalt());
    }

    @AssertArg
    @Override
    public boolean existsByValueInDict(@ArgHasLength String value, @ArgNonNull Integer dictId, @ArgNonNull Integer withoutId) {
        return eDictValueRepository.existsAllByDict_IdAndValueAndDelSaltAndIdNot(dictId, value, getExistsDelSalt(), withoutId);
    }

    @AssertArg
    @Override
    public boolean existsByIdInDict(@ArgNotZero Integer dictId, @ArgNotZero Integer id) {
        return eDictValueRepository.existsAllByIdAndDict_IdAndDelSalt(id, dictId, getExistsDelSalt());
    }

    @AssertArg
    @Override
    public Optional<EDictValue> findByValueInDict(@ArgHasLength String value, @ArgNotZero Integer dictId) {
        return eDictValueRepository.findFirstByDict_IdAndValueAndDelSalt(dictId, value, getExistsDelSalt());
    }

    @Override
    public Optional<EDictValue> findByIdInDict(Integer id, Integer dictId) {
        return eDictValueRepository.findFirstByDict_IdAndIdAndDelSalt(dictId, id, getExistsDelSalt());
    }

    @AssertArg
    @Override
    public Page<EDictValue> findPageByDictIdAndValueLike(@ArgNonNull Pageable pageable, @ArgNotZero Integer dictId, String valueLike) {
        Specification<EDictValue> spec = valueLike == null ? getDictIdEqSpec(dictId) : getDictIdEqSpec(dictId).and(getValueLikeSpec(valueLike));
        return eDictValueRepository.findAll(spec, pageable);
    }

    @AssertArg
    @Override
    public List<EDictValue> findAllByDictIdAndValueLike(@ArgNotZero Integer dictId, String valueLike) {
        Specification<EDictValue> spec = valueLike == null ? getDictIdEqSpec(dictId) : getDictIdEqSpec(dictId).and(getValueLikeSpec(valueLike));
        return eDictValueRepository.findAll(spec);
    }

    @Override
    public Integer getExistsDelSalt() {
        return (Integer) eDictValueRepository.getDefaultExistsDelSalt();
    }

    private static Specification<EDictValue> getDictIdEqSpec(Integer dictId) {
        return (Specification<EDictValue>) (root, query, criteriaBuilder) -> criteriaBuilder.equal(root.get("dict").get("id"), dictId);
    }

    private static Specification<EDictValue> getValueLikeSpec(String valueLike) {
        return (Specification<EDictValue>) (root, query, criteriaBuilder) -> criteriaBuilder.like(root.get("value"), valueLike);
    }
}
